﻿using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using Net.System;
using Net.Client;
using ikcpcb = KCP.IKCPCB;
using static KCP.KCP;

namespace Net.Server
{
    /// <summary>
    /// kcp客户端对象
    /// </summary>
    public unsafe class KcpPlayer : ClientPeerBase
    {
        private ikcpcb* kcp;
        private IntPtr user;
        private Socket server;

        internal void Set(Socket server)
        {
            this.server = server;
            var handle = GCHandle.Alloc(this, GCHandleType.Normal);
            user = GCHandle.ToIntPtr(handle);
            kcp = ikcp_create(0, (void*)user);
            kcp->output = &Output;
            ikcp_wndsize(kcp, 128, 128);
            ikcp_nodelay(kcp, 1, 10, 2, 1);
            kcp->rx_minrto = 10;
            kcp->fastresend = 1;
        }

        public static unsafe int Output(byte* buffer, int length, ikcpcb* kcp, void* user)
        {
            var client = GCHandle.FromIntPtr((nint)user).Target as KcpPlayer;
            client.trafficStatistics.SentBytesPerSecond += length;
            client.trafficStatistics.SentPacketsPerSecond++;
#if NET5_0_OR_GREATER
            var span = new ReadOnlySpan<byte>((void*)buffer, length);
            return client.server.SendTo(span, SocketFlags.None, client.RemotePoint);
#else
            var bytes = new byte[length];
            Unsafe.CopyBlock(ref bytes[0], ref *buffer, (uint)length);
            return client.server.SendTo(bytes, SocketFlags.None, client.RemotePoint);
#endif
        }

        public override void ReceiveHandler()
        {
            if (ReceiveList.TryDequeue(out var segment))
            {
                fixed (byte* data = &segment.Buffer[0])
                    ikcp_input(kcp, data, segment.Count);
                BufferPool.Push(segment);
            }
            int len;
            while ((len = ikcp_peeksize(kcp)) > 0)
            {
                segment = BufferPool.Take(len);
                fixed (byte* p1 = &segment.Buffer[0])
                {
                    segment.Count = ikcp_recv(kcp, p1, len);
                    ReceiveHandler(ref segment);
                    BufferPool.Push(segment);
                }
            }
        }

        protected override void SendByteData(ISegment buffer)
        {
            fixed (byte* p = &buffer.Buffer[0])
            {
                int count = ikcp_send(kcp, p, buffer.Count);
                if (count < 0)
                    OnSendErrorHandle?.Invoke(buffer);
            }
        }

        protected override bool CheckIsConnected()
        {
            if (!Connected)
            {
                if (Environment.TickCount >= ReconnectTimeout)
                    RemoveClient();
                return false;
            }
            return true;
        }

        public override void OnNetworkTick()
        {
            base.OnNetworkTick();
            ikcp_update(kcp, (uint)Environment.TickCount);
        }

        ~KcpPlayer()
        {
            if (kcp == null)
                return;
            ikcp_release(kcp);
            kcp = null;
            GCHandle.FromIntPtr(user).Free();
        }
    }
}
